import java.util.Stack;
import java.util.NoSuchElementException;
import java.util.EmptyStackException;

public class HistorizedLaby implements ILaby
{
	protected final Labyrinthe lab; //internal object (this class is a wrapper)
	protected final Stack<Position> hist_p; //positions; each has row and col attributes
	protected final Stack<Integer> hist_m; //moves indexes
	public static final int[] MOVES = {Labyrinthe.UP, Labyrinthe.RIGHT, Labyrinthe.DOWN, Labyrinthe.LEFT};
	
	public HistorizedLaby(Labyrinthe lab)
	{
		//If super() is not called manually, the default constructor will be used.
		//super(new Position(0, 0), new Position(0, 0)); //fake values
		
		this.lab = lab;
		this.hist_p = new Stack<Position>();
		this.hist_p.push(new Position(0, 0)); //initial relative position
		this.hist_m = new Stack<Integer>();
	}
	
	public void reset()
	{
		lab.reset();
		hist_p.clear();
		hist_p.push(new Position(0, 0)); //initial relative position
		hist_m.clear();
	}
	
	protected static String dirToString(int direction)
	{
		switch (direction)
		{
			case Labyrinthe.UP: return "up";
			case Labyrinthe.RIGHT: return "right";
			case Labyrinthe.DOWN: return "down";
			case Labyrinthe.LEFT: return "left";
			default: return "?";
		}
	}
	
	protected static int dirToIndex(int direction) throws NoSuchElementException
	{
		for (int i=0; i<MOVES.length; i++)
			if (direction == MOVES[i]) return i;
		throw new NoSuchElementException();
	}
	
	protected Position getAbsolute(int i)
	{
		try
		{
			Position lp = hist_p.peek();
			switch (i)
			{
				case 0: return new Position(lp.row+1, lp.col); //up
				case 1: return new Position(lp.row, lp.col+1); //right
				case 2: return new Position(lp.row-1, lp.col); //down
				case 3: return new Position(lp.row, lp.col-1); //left
				default: return null;
			}
		}
		catch (EmptyStackException e)
		{
			return null;
		}
	}
	
	protected boolean visited(Position p)
	{
		for (Position x: hist_p)
			if (x.row==p.row && x.col==p.col)
				return true; //found!
		return false; //not found
	}
	
	public void undo() throws NoSuchElementException
	{
		int m = (int)hist_m.pop();
		hist_p.pop(); //discard it
		lab.go(MOVES[(m+2)%4]);
	}
	
	//returns a copy of the actual list
	public int[] getMoves()
	{
		int[] ret = new int[hist_m.size()];
		int i = 0;
		for (int m: hist_m)
		{
			ret[i] = m;
			i++;
		}
		return ret;
	}
	
	public int getMovesCount()
	{
		return hist_m.size();
	}
	
	public void displayMoves()
	{
		if (hist_m.size()==0)
			System.out.println("No move.");
		else
		{
			int ct=1, last=(int)hist_m.get(0), cur;
			for (int i=1; i<hist_m.size(); i++)
			{
				if (last==hist_m.get(i))
					ct++;
				else
				{
					System.out.println(ct+"x "+dirToString(last));
					//next one
					ct = 1;
					last = hist_m.get(i);
				}
			}
			System.out.println(ct+"x "+dirToString(last));
			System.out.println("Total: "+hist_m.size()+" moves");
		}
	}
	
	
	//Normal interface
	
	public boolean go(int dir)
	{
		int i = dirToIndex(dir);
		Position p = getAbsolute(i);
		if (visited(p) || !lab.go(dir)) return false; //denied
		hist_p.add(p);
		hist_m.add(new Integer(i));
		return true;
	}
	
	public boolean isWall(int dir) {return lab.isWall(dir);}
	public void affichage() {lab.affichage();}
	public int getCols() {return lab.getCols();}
	public int getRows() {return lab.getRows();}
	public boolean isExit() {return lab.isExit();}
	
	//As I inherited Labyrinthe, no @Override would be needed because 
	//there is no static method overloading in Java (only masking).
	public static HistorizedLaby laby1() {return new HistorizedLaby(Labyrinthe.laby1());}
	public static HistorizedLaby laby2() {return new HistorizedLaby(Labyrinthe.laby2());}
	public static HistorizedLaby laby3() {return new HistorizedLaby(Labyrinthe.laby3());}
	public static HistorizedLaby laby4() {return new HistorizedLaby(Labyrinthe.laby4());}
}
